package fr.lip6.meta.ple.featureIdentification;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;


import fr.lip6.meta.tools.Trigger;


/**
 * Is a Feature with a set of other FeautureNodes as children. Used for
 * creating trees where each node is a set of triggers.
 * 
 * @author Luz
 */
public class FeatureNode extends Feature {
	
	/**
	 * For avoiding warnings
	 */
	private static final long serialVersionUID = 7L;
	
	/**
	 * The children
	 */
	private ArrayList<FeatureNode> children = new ArrayList<FeatureNode>();
	
	/**
	 * Basic constructor, creates one node without children
	 * @param ft - the list of triggers
	 */
	public FeatureNode (Collection<Trigger> ft) {
		super(ft);
	}
	
	/**
	 * Constructor
	 */
	public FeatureNode () {
		super();
	}
	
	/**
	 * Constructor with triggers and ids
	 * @param ft - list of triggers
	 * @param ids - identifiers
	 */
	public FeatureNode (Collection<Trigger> ft, Collection<Integer> ids) {
		super(ft);
		prodIds.addAll(ids);
	}
	
	/**
	 * Getter for the children
	 * @return the list of children
	 */
	public ArrayList<FeatureNode> getChildren() {
		return children;
	}
	
	/**
	 * Creates a node containing the common triggers and saving the others
	 * as children nodes
	 * @param products - a collection of products
	 */
	public static FeatureNode featureNodeFromProducts (List<Product>
			products) {
		
		FeatureNode node = new FeatureNode();
		Collection<Trigger> common = Feature.getCommonLine(products);
		if (common.isEmpty()) throw new UnsupportedOperationException();
		node.addAll(common);
		for (Product p : products) {
			node.prodIds.add(p.getId());
			Feature fCopy = Feature.featureFromProduct(p);
			fCopy.removeAll(common);
			node.children.add(new FeatureNode(fCopy, fCopy.prodIds));
		}
		node.removeEmptyChildren ();
		return node;
	}

	/**
	 * Executes the algorithm with a given feature.
	 * 1- Search the children who contain the feature, that is, the compatible
	 * children
	 * 2- Creates a new node representing that feature
	 * 3- Subtracts the feature from the compatible children, removes the link
	 * from this node and sets them as children of the new node
	 * @param set - the common triggers to merge
	 * @return true if a new node has been created
	 */
	public boolean expand (Collection<Trigger> set) {
		//Pre-conditions
		if (set == null) return false;
		if (set.isEmpty()) return false;
		
		//Search the compatible children
		Set<FeatureNode> compatibleChildren = compatibleChildren (set);
		if (compatibleChildren.isEmpty()) return false;
		
		//Merge into a new node
		FeatureNode newNode = new FeatureNode(set);
		for (FeatureNode child : compatibleChildren)
			newNode.prodIds.addAll(child.prodIds);
		children.removeAll(compatibleChildren);
		children.add(newNode);
		newNode.children.addAll(compatibleChildren);
		
		//Extract common
		newNode.removeFromChildren(set);
		newNode.removeEmptyChildren();
		
		return true;
	}
	
	
	
	/**
	 * Subtracts a set from the children
	 * @param set
	 */
	private void removeFromChildren (Collection<Trigger> set) {
		for (FeatureNode child : children) {
			child.removeAll(set);
		}
	}
	
	/**
	 * Removes the children who contain no triggers
	 */
	private void removeEmptyChildren () {
		ArrayList<FeatureNode> forRemove = new ArrayList<FeatureNode>();
		for (FeatureNode child : children) {
			if (child.isEmpty())
				forRemove.add(child);
		}
		for (FeatureNode r : forRemove) {
			children.remove(r);
		}
	}
	
	/**
	 * Looks for the children containing the given feature
	 * @param set - a feature
	 * @return a subset of the children
	 */
	private LinkedHashSet<FeatureNode> compatibleChildren (Collection<Trigger> set) {
		LinkedHashSet<FeatureNode> subset = new LinkedHashSet<FeatureNode>();
		for (FeatureNode child : children) {
			if (child.containsAll(set))
				subset.add(child);
		}
		return subset;
	}
	
	/**
	 * Represents the last id given creating a LTS
	 */
	@SuppressWarnings("unused")
	private static int stateId = 0;
	
	
	
	
	/**
	 * String-representation with a given indentation
	 * @param leng - the size of the indentation
	 * @return the string
	 */
	@SuppressWarnings("unused")
	private String toStringWithSpace(int leng) {
		String s = "";
		for (int i=0; i<leng; i++) {
			s += "  ";
		}
		s += super.toString();
		s += "\n";
		for (FeatureNode child : children) {
			s += child.toStringWithSpace(leng+1);
		}
		return s;
	}

}
